/**
    @file util.hpp
    @brief Helper functions
*/
#ifndef PM3_UTIL_HPP
#define PM3_UTIL_HPP

#include <string>
#include <sstream>

#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include <OgreSceneNode.h>

#include "interface.hpp"

//! Startingpoint tag
#define OT_START    "start"
//! Finish tag
#define OT_FINISH   "finish"
//! Rooms with this tag won't be connected to the rest
#define OT_IGNORE   "ignore"
//! Rooms with this tag can be passed
#define OT_PASSAGE  "passage"

//! Integer type string
#define STR_INT     "int"
//! Float type string
#define STR_FLOAT   "float"
//! Boolean type string
#define STR_BOOLEAN "bool"
//! String type string
#define STR_STRING  "string"

namespace pm3 {

    //! Utility functions
    namespace util {
        //! Direction flags
        enum DIRECTIONS {
            //! No direction (a complete dead-end)
            D_NONE = 0,
            //! North
            D_N = 1,
            //! East
            D_E = 2,
            //! South
            D_S = 4,
            //! West
            D_W = 8,
            //! Up
            D_U = 16,
            //! Down
            D_D = 32,
            //! Last bit
            D_LAST = 32,
            //! All combinations
            D_ALL = 63
        };
        //! Direction indexes
        enum DIRECTION_IDS {
            //! North
            DID_N = 0,
            //! East
            DID_E = 1,
            //! South
            DID_S = 2,
            //! West
            DID_W = 3,
            //! Up
            DID_U = 4,
            //! Down
            DID_D = 5,
            //! Number of directions
            DID_LAST = 6
        };

        //! Allowed tag delimiters
        const std::string tag_delimiters = ", ";
        //! Typical tag delimiter to be used
        const std::string tag_delimiter = ", ";

        /**
            @brief Tokenizes a string
            @param[in] r_string Reference to the string to be tokenized
            @param[in] r_delims Reference to the delimiters that separate the tokens
            @return A vector of string tokens
        */
        std::vector<std::string> tokenize( const std::string &r_string, const std::string &r_delims );

        /**
            @brief Converts a string directions to an integer form
            @param[in] r_string Reference to the string to be converted
            @return An integer ORed with direction tags
        */
        int str_to_directions( const std::string &r_string );

        /**
            @brief Converts directions from integer form into a string
            @param[in] i_directions Directions ORed
            @return String directions
        */
        std::string directions_to_str( int i_directions );

        /**
            @brief Gets a random direction from all available directions
            @param[in] i_directions All available directions ORed
            @return Only 1 direction
        */
        int get_rand_direction( int i_directions );

        /**
            @brief Reverses directions (N -> S, E -> W and vice versa)
            @param[in] i_directions
            @return Reversed direction flags
        */
        int reverse_directions( int i_directions );

        /**
            @brief Gets the id of a direction
            @param[in] i_dir Direction flag
            @return Direction id
        */
        int get_direction_id( int i_dir );

        /**
            @brief Gets the quaternion of the first direction marked in the argument
            @param[in] i_direction The direction
            @return Ogre quaternion
        */
        Ogre::Quaternion direction_to_quat( int i_direction );

        /**
            @brief Checks if a string contains a specific tag
            @param[in] r_string Reference to the string to be checked
            @param[in] r_tag Reference to the tag to search for
            @return True if it contains the tag, otherwise false
        */
        bool has_tag( const std::string &r_string, const std::string &r_tag );

        /**
            @brief Adds a tag to the string (a taglist)
            @param[in] r_string Reference to the string
            @param[in] r_tag Reference to the tag to be added
            @return The string with the tag added
        */
        std::string add_tag( const std::string &r_string, const std::string &r_tag );

        /**
            @brief Adds tags to the string (a taglist)
            @param[in] r_string Reference to the string
            @param[in] r_tags Reference to the tags to be added
            @return The string with the tags added
        */
        std::string add_tags( const std::string &r_string, const std::string &r_tags );

        /**
            @brief Removes a tag from the string (a taglist)
            @param[in] r_string Reference to the string
            @param[in] r_tag Reference to the tag to be removed
            @return The string with the tag removed
        */
        std::string remove_tag( const std::string &r_string, const std::string &r_tag );

        /**
            @brief Removes tags from the string (a taglist)
            @param[in] r_string Reference to the string
            @param[in] r_tags Reference to the tags to be removed
            @return The string with the tags removed
        */
        std::string remove_tags( const std::string &r_string, const std::string &r_tags );

        /**
            @brief Converts string to integer
            @param[in] r_str Reference to the string to be converted
            @return Integer
        */
        int str_to_int( const std::string &r_str );

        /**
            @brief Converts string to float
            @param[in] r_str Reference to the string to be converted
            @return Float
        */
        float str_to_float( const std::string &r_str );

        /**
            @brief Converts to string
            @param[in] o Reference to the object to be converted
            @return String
        */
        template<typename T>
        inline std::string to_str( const T &o ) {
            std::ostringstream oss;

            oss << o;

            return oss.str ();
        }

        /**
            @brief Composes a name suffix from a grid position vector
            @param[in] r_vec Reference to a grid position vector
            @return Name suffix
        */
        std::string pos_suffix( const Ogre::Vector3 &r_vec );

        /**
            @brief Returns the minimum of two objects
            @param[in] a Reference to the first object
            @param[in] b Reference to the second object
            @return The minimum
        */
        template<typename T>
        inline T min( const T &a, const T &b ) {
            if (a < b)
                return a;
            return b;
        }

        /**
            @brief Returns the maximum of two objects
            @param[in] a Reference to the first object
            @param[in] b Reference to the second object
            @return The maximum
        */
        template<typename T>
        inline T max( const T &a, const T &b ) {
            if (a > b)
                return a;
            return b;
        }

        /**
            @brief Returns the minimum of three objects
            @param[in] a Reference to the first object
            @param[in] b Reference to the second object
            @param[in] c Reference to the third object
            @return The minimum
        */
        template<typename T>
        inline T min( const T &a, const T &b, const T &c ) {
            if (a < b && a < c)
                return a;
            else if (c < b && c < a)
                return c;
            return b;
        }

        /**
            @brief Returns the maximum of three objects
            @param[in] a Reference to the first object
            @param[in] b Reference to the second object
            @param[in] c Reference to the third object
            @return The maximum
        */
        template<typename T>
        inline T max( const T &a, const T &b, const T &c ) {
            if (a > b && a > c)
                return a;
            else if (c > b && c > a)
                return c;
            return b;
        }

        /**
            @brief Get string type
            @param[in] r_str Reference to the string to be classified
            @return STR_INT, STR_FLOAT, STR_BOOLEAN or STR_STRING
        */
        std::string get_str_type( const std::string &r_str );

        /**
            @brief Extracts directory from path
            @param[in] r_path Reference to filepath
            @return Directory with the slash included
        */
        std::string extract_dir( const std::string &r_path );

        /**
            @brief Strips directory from path
            @param[in] r_path Reference to filepath
            @param[in] r_dir Reference to the directory to be stripped
            @return Directory with the slash included
        */
        std::string strip_dir( const std::string &r_path, const std::string &r_dir );

        /**
            @brief Checks if path contains a specific directory
            @param[in] r_path Reference to filepath
            @param[in] r_dir Reference to the directory to be checked for
            @return True if the path contains the directory, false otherwise
        */
        bool has_dir( const std::string &r_path, const std::string &r_dir );

        /**
            @brief Rounds a float
            @param[in] f The float to be rounded
            @return Result
        */
        float round( float f );

        /**
            @brief Rounds a vector
            @param[in] r_v Reference to the vector to be rounded
            @return Result
        */
        Ogre::Vector3 round( const Ogre::Vector3 &r_v );

        /**
            @brief Traces parents of a scenenode
            @param[in] p_node Pointer to the node for which parents are traced
            @return A string consisting of parents
        */
        std::string trace_parents( Ogre::PCZSceneNode *p_node );

        /**
            @brief Traces parent zones of a scenenode
            @param[in] p_node Pointer to the node for which parent zones are traced
            @return A string consisting of parent zones
        */
        std::string trace_zones( Ogre::PCZSceneNode *p_node );

        /**
            @brief Indents text lines
            @param[in] r_indent Reference to the indent string
            @param[in] r_text Reference to the text string
            @return Indented text string
        */
        std::string indent_text( const std::string &r_indent, const std::string &r_text );

        /**
            @brief Converts color to HTML-like hex RGB string
            @param[in] r_color Reference to the color
            @return Color hex string
        */
        std::string color_to_hex( const Ogre::ColourValue &r_color );
    }
}

#endif

